/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.force4spring;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.force4spring.support.ForceSession;
import org.force4spring.support.SessionFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;


/**
 * Callback for resource cleanup at the end of a Spring-managed JTA transaction,
 * that is, when participating in a JtaTransactionManager transaction.
 * 
 * @author Juergen Hoeller
 * @author Max Rudman
 * @see SessionFactoryUtils
 * @see org.springframework.transaction.jta.JtaTransactionManager
 */
class SpringSessionSynchronization extends TransactionSynchronizationAdapter
		implements Ordered {
	private static final Log log = LogFactory
			.getLog(SpringSessionSynchronization.class);

	private final SessionHolder sessionHolder;

	private final SessionFactory sessionFactory;

	private final boolean newSession;

	private boolean holderActive = true;

	public SpringSessionSynchronization(SessionHolder sessionHolder,
			SessionFactory sessionFactory, boolean newSession) {

		this.sessionHolder = sessionHolder;
		this.sessionFactory = sessionFactory;
		this.newSession = newSession;
	}

	public int getOrder() {
		return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
	}

	public void suspend() {
		if (this.holderActive) {
			TransactionSynchronizationManager
					.unbindResource(this.sessionFactory);
		}
	}

	public void resume() {
		if (this.holderActive) {
			TransactionSynchronizationManager.bindResource(this.sessionFactory,
					this.sessionHolder);
		}
	}

	public void beforeCommit(boolean readOnly) throws DataAccessException {
		if (!readOnly) {
			// read-write transaction -> flush the Apex Session
			log.debug("Flushing Apex Session on transaction synchronization");
			ForceSession session = this.sessionHolder.getSession();
			try {
				session.flush();
			} catch (Exception e) {

			}
		}
	}

	public void beforeCompletion() {
		if (this.newSession) {
			// Default behavior: unbind and close the thread-bound Apex Session.
			TransactionSynchronizationManager
					.unbindResource(this.sessionFactory);
			this.holderActive = false;
		}
	}

	public void afterCompletion(int status) {
		// Close the Force Session here if necessary
		// (closed in beforeCompletion in case of TransactionManagerLookup).
		if (this.newSession) {
			ForceSession session = this.sessionHolder.getSession();
			SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
		}
		
		if (!this.newSession && status != STATUS_COMMITTED) {
			// Clear all pending inserts/updates/deletes in the Session.
			// Necessary for pre-bound Sessions, to avoid inconsistent state.
			this.sessionHolder.getSession().clear();
		}
		if (this.sessionHolder.doesNotHoldNonDefaultSession()) {
			this.sessionHolder.setSynchronizedWithTransaction(false);
		}
	}

}
